home *** CD-ROM | disk | FTP | other *** search
/ CD ROM Paradise Collection 4 / CD ROM Paradise Collection 4 1995 Nov.iso / edit / aaem95ma.zip / EM.CC < prev    next >
C/C++ Source or Header  |  1995-02-27  |  45KB  |  828 lines

  1. /* This file is EM.CC */
  2. #include "em.h"
  3. /*-----*/
  4. char CW[strsize],disp[256]; jmp_buf *bad=0; /* FILE *debug; */
  5. /*----- is char in string? */
  6. int val::operator>>(reg char c){reg char*S=s,*T;
  7. for(T=S+n;T>=S;) if(c==*--T) return 1; return 0;}
  8. /*----- val==string? */
  9. int val::operator==(reg char*t){if(!t?:!s) return 0; reg int i,m=n;
  10. reg char*u=s; for(i=0;i<m;i++) if(u[i]!=t[i]) return 0; return t[m]==0;}
  11. /*-----*/
  12. /* char* copy_text(reg char*s,reg char*t,reg int n){reg int i;
  13. for(i=0;i<n;i++) s[i]=t[i]; return s;} */
  14. /*-----*//* fast move s[0:n-1]=t[0:n-1] */
  15. char* copy_text(char*s,char*t,int n){
  16. asm("pushl %esi"); asm("pushl %edi"); asm("cld");
  17. asm("movl 8(%ebp),%edi"); asm("movl 12(%ebp),%esi"); asm("movl 16(%ebp),%ecx");
  18. asm("rep"); asm("movsb"); asm("movl 8(%ebp),%eax");
  19. asm("popl %edi"); asm("popl %esi");}
  20. /*-----*/
  21. Text killring[16]; short nkill=0;
  22. gp_cur cursor(0,0); /* the cursor */
  23. char *Moan=0,*Display=0; line*dustbin=0;
  24. /*----- if line is blank */
  25. int line::blank(){reg int i,m=n; char*t=s,j;
  26. for(i=0;i<m;i++) if((j=t[i])==' '?0:j!=9) return 0; return 1;};
  27. /*----- new line with specified text */
  28. line::line(char*s){nullline(); *this=val(s,strlen(s));};
  29. line::line(char*s,int n){nullline(); *this=val(s,n);};
  30. line::line(line*L){nullline(); *this=val(L->s,L->n); no_cr(L->no_cr());};
  31. /*-----*/
  32. line *line0,*modeline; /* dummy lines */
  33. screenline Sl[256];
  34. /*----- initialize buffer */
  35. void buffer::initbuffer(){next=0; nrows=ncols=0; row1=lastrow=refresh=0; name=0;
  36. sortcol=truncated=invisible=readonly=changed=oldch=lastrow=stback=0;
  37. rmargin=71; startc=0; bound=val(); dotcc=dotcc2=-1; col.init();
  38. tabtype=Case=1; twod=overlay=wrap=longlines=skip_after_word=0;
  39. text.line(); text.next=text.prev=new line();
  40. start=mark(text.next,0); dot=start; dot1=start; dot2=start; olddot=start;
  41. start.prev=0; start^olddot; olddot^dot1; dot1^dot2; dot2^dot; dot.next=0;}
  42. /*-----*/
  43. buffer *buf0, *bhead=0, *B=0; int bcount=0;    /* B -> current buffer */
  44. line *T; /* entry point of circle of lines currently being handled */
  45. char T1w[magicsize]={0},T2w[magicsize]={0}; val T1t(T1w,0),T2t(T2w,0);
  46. int needswrap=0,obtype,prevobtype=ob_other; region lastyank;
  47. buffer*window[16]; int nwindows=1,currentwindow=0; byte ccc;
  48. /*---------- interface with screen */
  49. long _ax,_bx,_cx,_dx,_si,_di,_bp,_es; short _flags; int nextch=0;
  50. byte gp_Mode,gp_Rows,gp_Cols;
  51. short gp_Attr=White,gp_Attr_def=White; /* on black */
  52. /*-----*/
  53. void int10() {__SR(); asm("int $0x10"); __RR();}
  54. void int16() {__SR(); asm("int $0x16"); __RR();}
  55. void int21() {__SR(); asm("int $0x21"); __RR();}
  56. void int31() {__SR(); asm("int $0x31"); __RR();}
  57. void int33() {__SR(); asm("int $0x33"); __RR();}
  58. /*----- is a key waiting to be read? */
  59. int typahead() {if(nextch) return 1; _ax=0xb00; int21(); return _ax&255;}
  60. /*----- swop foreground and background colours at a screen position */
  61. void counterchange(uns short&x){x=(x&0x88ff)|((x&0x0700)<<4)|((x&0x7000)>>4);}
  62. /*-----*/
  63. byte get__key(){_ax=0x700; int21(); return _ax&255;}
  64. /*-----*/
  65. int get_key(){return get__key()?:-get__key();}
  66. /*-----*/
  67. int loseip(){A: _ax=0xb00; int21(); if(_ax&255) {get__key(); goto A;} return 1;}
  68. /*-----*//* Put a char into DOS's input buffer. Returns 0 if fail */
  69. int inject(int ch){_ax=0x500;
  70. _cx=ch<0?((-ch)&0xff)<<8:((ch&0xff)<<8)+(ch&0xff); int16(); return !(_ax&1);}
  71. /*-----*/
  72. int getkey(){int i; uns short s,*t; if(nextch) {i=nextch; nextch=0; return i;}
  73. if(B) {t=&scr(B->lastrow+1,-1); s=*t;
  74.     if(Jerry.mc) *t=sch('m',Orange+(White<<4)); counterchange(*t);}
  75. if(ccc) counterchange(scr(cursor.r,cursor.c));
  76. A: i=get_key(); if(!Jerry.mc) if(i==-mousemove) goto A;
  77. if(ccc) counterchange(scr(cursor.r,cursor.c));
  78. if(B) *t=s;
  79. if(B) if(i) {line*L=B->dot.r,*M; while(M=L->prev) L=M;
  80.     if(L!=B->text.next) Moan="BUG: buffer.dot.r pointing to wrong buffer";}
  81. return i;}
  82. /*-----*/
  83. int getkey_nowait(){return typahead()?getkey():0;}
  84. /*----- print char on screen, obeying ctrl chars */
  85. void printch(char c) {if(c=='C'-64) return;
  86. if(c==LF) {_ax=0x200; _dx=CR; int21();} _ax=0x200; _dx=c; int21();}
  87. /*-----*/
  88. void wr(char*s){while(*s) printch(*s++);}
  89. /*-----*/
  90. int beep(){_ax=0x0200; _dx=7; int21(); return 0;}
  91. /*----- = current screen mode */
  92. int gp_mode(void) {_ax=0xf00; int10(); return _ax&255;}
  93. /*----- screen mode := m */
  94. void gp_mode(char m) {_ax=m&255; int10(); gp_Attr=gp_Attr_def;
  95.   if(m!=gp_mode()) ps("\007error: this computer has no mode 0x%1x\n",m);}
  96. /*----- move cursor to c */
  97. void gp_cursor(gp_cur c) {_ax=0x200; _bx=0; _dx=*((short*)&c); int10();}
  98. /*----- = where cursor is on screen */
  99. gp_cur gp_cursor(void) {_ax=0x300; _bx=0; int10(); return *((gp_cur*)&_dx);}
  100. /*----- clear screen line i cols j to J */
  101. void gp_clear(int i,int j/*=0*/,reg int J/*=gp_Cols-1*/){
  102.   reg int k; reg uns short*L=Sl[i].sa; for(k=j;k<=J;k++) L[k]=sch(' ',White);}
  103. /*----- video-reverse from gp_Attr */
  104. inline byte revattr(char a){return ((a&7)<<4)|((a>>4)&7);}
  105. /*----------*//* error exit */
  106. volatile void MOAN(char *m) {beep(); gp_Attr=White; Moan=m; longjmp(*bad,1);}
  107. /*----- get current drive */
  108. byte drive(){_ax=0x1900; int21(); return _ax+'a';}
  109. /*----- get current directory for drive c ('@' = current drive, here) */
  110. void getcurrentdir(char c,char*s) {_dx=c&31; _si=(long)s; _ax=0x4700; __SR();
  111. asm("int $0x21"); __RR(); if(_carry)
  112.   MOAN("no such drive, or bad drive, or no good floppy on this drive");}
  113. /*-----make a full pathname, replace / by \, convert to lowercase */
  114. char* fullfilename(char*full,char*name) {int i,j,k,l,m,n,p,q; byte c;
  115. char *s,*t,Dir[strsize],cw[strsize],*pf[128];
  116. if(!name[0]) {full[0]=0; return full;}
  117. if(name[0]=='(') {strcpy(full,name); return full;}
  118. for(s=cw,t=name;c=*t;t++) if(c>32) if(c<'A' ?0: c<='Z') *s++=c+32; else *s++=c;
  119. *s=0; n=strlen(cw); /**** relies on ctrl chars=0-31, space=32, lc=uc+32 **/
  120. s=cw; i=*s; if(i<'a' ?0: i>'z' ?0: s[1]==':') s+=2; else i=drive();
  121. Dir[0]=i; Dir[1]=':';
  122. if(*s=='/'?:*s=='\\') Dir[2]=0;
  123. else {Dir[2]='\\'; for(j=3;j<256+2;j++) Dir[j]=0; getcurrentdir(i,Dir+3);}
  124. i=strlen(Dir); Dir[i]='\\'; strcpy(Dir+i+1,s);
  125. for(pf[0]=s=Dir,q=1;*s;s++) if(*s=='/'?1:*s=='\\') {*s=0; pf[q++]=s+1;}
  126. for(i=0;i<q;i++) if(!(s=pf[i])[0] ?1: (s[0]!='.'?0:!s[1])) pf[i]=0; /* \\ \.\ */
  127. SQUEEZE: for(p=i=0;i<q;i++) {if(s=pf[i]) pf[p++]=s; /* remove nulls */} q=p;
  128. for(i=0;i<q;i++) if((s=pf[i])[0]=='.') if(s[1]=='.') if(!s[2]) { /* \..\ */
  129.     pf[i]=0; if(i>1) pf[i-1]=0; goto SQUEEZE;}
  130. for(i=1;i<q;i++) {k=m=-1; l=strlen(s=pf[i]); /* check each name segment */
  131.     if(s[0]=='.') continue; /* starts with dot, illegal on PC */
  132.     for(j=0;j<l;j++) if(s[j]=='.') {k=j; break;} /* find first dot */
  133.     if(k<0) {if(l>8) s[8]=0; continue;} /* no dot: shorten to 8 chars */
  134.     for(j++;j<l;j++) if(s[j]=='.') {m=j; break;} /* find second dot */
  135.     if(m>0) continue; /* >1 dots, illegal on PC */
  136.     if(l>k+4) s[l=k+4]=0; /* shorten suffix to 3 chars */
  137.     if(k>8) for(m=k-8,j=k;j<=l;j++) s[j-m]=s[j];} /* shorten stem to 8 chars */
  138. /*for(i=0;i<q;i++){DEBUG;fprintf(debug,"pf[%1d]='%s'\n",i,pf[i]?:"(null)");GUBED;}*/
  139. strcpy(full,pf[0]); for(i=1;i<q;i++) {
  140.     n=strlen(full); full[n]='\\'; strcpy(full+n+1,pf[i]);}
  141. return full;}
  142. /*----- compare n chars from t to s */
  143. int comptext(reg char*s,reg char*t,int n,int nocase/*=0*/){reg char*u;
  144. if(!n) return 1; if(!s) return 0; if(!t) return 0;
  145. if(nocase) {for(u=s+n;s<u;) if(to_upper(*s++)!=to_upper(*t++)) return 0;}
  146. else {for(u=s+n;s<u;) if(*s++!=*t++) return 0;} return 1;}
  147. /*---------- <marks> */
  148. /*----- how many marks currently defined on this buffer */
  149. int buffer::nmarks(){
  150.     reg mark *M; reg int n=0; for(M=dot.next;M;M=M->next,n++); return n;}
  151. /*----- find mark N. -ve N = count from bottom of stack */
  152. mark&buffer::Mark(int N){reg mark *M; reg int i; if(!N) N=1;
  153. if(N==999) return dot2;
  154. if(N>0) {for(M=B->dot.next  ,i= 1;i<N;i++,M=M->next) if(!M) break;}
  155. else    {for(M=B->start.prev,i=-1;i>N;i--,M=M->prev) if(!M) break;}
  156. if(!M) if(N>0) if(N==i) if(N<17) {B->dot.hsup(); return *B->start.prev;}
  157. if(!M) {pr(CW,"mark %d not defined yet",N); MOAN(CW);} return *M;}
  158. /*----- pushmark */
  159. void mark::push(){reg mark *M,*P; M=new mark(*this); M->prev=0;
  160. if(!(P=B->dot.next)) {B->dot.next=B->start.prev=M; M->next=0;}
  161. else {*M^*P; B->dot.next=M;}}
  162. /*----- push mark onto bottom of mark stack */
  163. void mark::hsup(){reg mark *M,*P; M=new mark(*this); M->next=0;
  164. if(!(P=B->start.prev)) {B->dot.next=B->start.prev=M; M->prev=0;}
  165. else {*P^*M; B->start.prev=M;}}
  166. /*----- popmark */
  167. void mark::pop(){reg mark *M;
  168. if(!(M=B->dot.next)) MOAN("this buffer's mark stack is already empty");
  169. *this=*M; if(B->dot.next=M->next) M->next->prev=0; else B->start.prev=0;
  170. delete M; return;}
  171. /*----- remove a mark */
  172. void buffer::rem1mark(){reg mark *M;
  173. if(!(M=dot.next)) MOAN("this buffer's mark stack is already empty");
  174. if(dot.next=M->next) M->next->prev=0; else start.prev=0; delete M; return;}
  175. /*---------- <lines> */
  176. /*----- count lines from start to this line */
  177. int line::lineno(){reg int i; reg line *L;
  178. for(i=0,L=this;L;i++,L=L->prev); return i;}
  179. /*----- insert line after L */
  180. line* operator/(line &L,val t) {reg line *N,*X,*T=&B->text; N=new line();
  181. X=L.next?:T; L-*N; *N-*X; T->next->prev=T->prev->next=0; *N=t; return N;}
  182. /*----- insert line before L */
  183. line* operator/(val t,line &L) {reg line *N,*X,*T=&B->text; N=new line();
  184. X=L.prev?:T; *X-*N; *N-L; T->next->prev=T->prev->next=0; *N=t; return N;}
  185. /*----- how to delete a line */
  186. void line::del(){if(dustbin) *this-*dustbin; else next=0; dustbin=this;}
  187. line::~line() {int i; if(s) delete s;}
  188. void emptydustbin(){reg line *J,*K;
  189.     for(J=dustbin;J;J=K) {K=J->next; delete J;} dustbin=0;}
  190. /*----- line=val : line gets new text */
  191. void line::operator=(val t){reg mark *M; reg int j; reg char *ss,*u;
  192. if(!t.n) {delete s; s=0; n=0; la(1); return;}
  193. ss=new char[j=roundup(t.n)]; la(1); delete s; s=ss; n=t.n;
  194. copy_text(ss,t.s,t.n); forallmarks(M) if(M->r==this) M->c<?=t.n;}
  195. /*----- line=int : change line.n */
  196. void line::operator=(reg int nn) {reg mark *M; reg int i; reg char *t;
  197. if(nn<n) forallmarks(M) if(M->r==this) M->c<?=nn;
  198. nn>?=0; i=roundup(nn);
  199. if(i==roundup(n)) n=nn;
  200. else if(la(1),!i) empty();
  201. else if(n>nn) {t=new char[i]; if(s) {copy_text(t,s,nn); delete s;} s=t; n=nn;}
  202. else {t=new char[i]; copy_text(t,s,n); delete s; s=t; n=nn;}
  203. /* if line lengthened, added part not initialized */
  204. forallmarks(M) if(M->r==this) M->c<?=nn; return;}
  205. /*----- line+=int : lengthen line */
  206. inline void line::operator+=(reg int nn) {*this=n+nn;}
  207. /*----- line-=int : shorten line */
  208. inline void line::operator-=(reg int nn) {*this=n-nn;}
  209. /*----- line+=char : append char to line */
  210. void line::operator+=(reg char C){reg int i; *this+=1; s[n-1]=C; la(1);}
  211. /*----- line+=line2 : append text of line2 to line */
  212. void line::operator+=(reg line&L) {reg int nn=n; *this=n+L.n;
  213. copy_text(s+nn,L.s,L.n); la(1); return;}
  214. /*----- -line : remove eol at start of this line */
  215. void line::operator-(){reg int i; reg mark *M;
  216. if(!prev) MOAN("chain lines: no previous line"); --*prev;}
  217. /*----- line-- : remove eol at end of this line */
  218. void line::operator--(){reg int i; reg mark *m; reg line *L=next,*M;
  219. if(!L) MOAN("chain lines: no next line"); i=n; *this+=*next; M=L->next;
  220. forallmarks(m) if(m->r==L) {m->r=this; m->c+=i;} no_cr(L->no_cr());
  221. if(M) *this-*M; else {next=0; B->text.prev=this;} L->del();}
  222. /*----- line-Text : remove eol at end of this line, in the Text */
  223. void line::operator-(reg Text&K){reg int i; reg line *L=next,*M; if(!L) return;
  224. *this+=*next; M=L->next; if(M) *this-*M; else {next=0; K.end=this;} L->del();}
  225. /*----- mark>>=int : move rest of line right n chars */
  226. void mark::operator>>=(reg int n){reg char*s,*t; if(n<0) *this<<=-n;
  227. else if(n) for(s=r->s+r->n-1-n,t=r->s+c;s>=t;s--) *(s+n)=*s;}
  228. /*----- mark<<=int : move rest of line left n chars */
  229. void mark::operator<<=(reg int n){reg char*s,*t; if(n<0) *this>>=-n;
  230. else if(n) for(s=r->s+r->n-n,t=r->s+c;t<s;t++) *t=*(t+n);}
  231. /*----- !mark : delete char at mark */
  232. void mark::operator!(){reg mark*M; c<?=r->n; r->la(1); B->changed=1;
  233. if(c==r->n) --*r;
  234. else {*this<<=1; *r-=1; forallmarks(M) if(M->r==r) if(M->c>c) M->c--;}}
  235. /*----- backspace-delete char at mark */
  236. void mark::bs() {reg mark*M; c<?=r->n; B->changed=1;
  237. if(!c) -(*r);
  238. else {c--; *this<<=1; *r-=1; r->la(1);
  239.     forallmarks(M) if(M->r==r) if(M->c>c) M->c--;}}
  240. /*----- mark=char : replace char at mark */
  241. void mark::operator=(reg char C) {c<?=r->n; r->la(1); B->changed=1;
  242. if(C==LF) *this/1;
  243. else if(c>=r->n) *r+=C;
  244. else r->s[c]=C;}
  245. /*----- mark+=char : insert char at mark */
  246. void mark::operator+=(reg char C){reg mark *M; c<?=r->n; r->la(1); B->changed=1;
  247. if(C==LF) *this/1;
  248. else if(c>=r->n) {*r+=C; c++;}
  249. else {*r+=1; (*this)>>=1; r->s[c]=C;
  250.     forallmarks(M) if(M->r==r) if(M->c>=c) M->c++;}}
  251. /*----- mark+=char* : insert string at mark */
  252. void mark::operator+=(reg char *C) {reg int i; reg char j;
  253. for(i=0;j=C[i];i++) (*this)+=j;}
  254. /*----- mark/int : insert eol. If i==0, mark at cut stays at eol before cut */
  255. line* mark::operator/(int i) {reg int cut,j; reg line *L,*N; L=r; reg mark *M;
  256. c<?=L->n; cut=c; N=*L/val(L->s+c,L->n-c); B->changed=1;
  257. j=N->no_cr(); N->no_cr(L->no_cr()); L->no_cr(j);
  258. if(i) {forallmarks(M) if(M->r==L) if(M->c>=cut) {M->r=N; M->c-=cut;}}
  259. else  {forallmarks(M) if(M->r==L) if(M->c> cut) {M->r=N; M->c-=cut;}}
  260. if(i!=2) if(B->start.r==N) B->start.r=L; *L=cut; L->la(1); return N;}
  261. /*----- line[int] : nth char in line */
  262. char line::operator[](reg int nn){return nn<0?' ':nn<n?s[nn]:LF;}
  263. /*----- change color of jth char of ith screen line */
  264. void recolor(reg int i,reg int j,int col){reg char*SL=(char*)Sl[i].sa;
  265. if(i>=0) if(i<gp_Rows) if(j>=0) if(j<gp_Cols) SL[j+j+1]=col;}
  266. /*-----*/
  267. void line::empty(){delete s; s=0; n=0; la(1);}
  268. /*-----*/
  269. char chwidth[256]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
  270. 0,0,5,3,5,5,5,6,6,3,4,4,5,5,3,5,3,5,5,5,5,5,5,5,5,5,5,5,3,3,5,5,5,5,6,6,6,6,6,6,
  271. 6,6,6,4,5,6,6,7,6,6,6,6,6,6,6,7,6,7,6,6,5,4,5,4,5,5,3,5,6,5,6,5,4,6,6,3,4,6,3,7,
  272. 6,5,6,6,5,5,4,6,6,7,5,6,5,4,3,4,5,0,6,6,5,5,5,5,5,5,5,5,5,3,3,3,6,6,6,7,7,5,5,5,
  273. 6,6,6,6,7,5,5,6,7,5,5,3,5,6,6,6,5,5,5,5,5,5,5,5,5,5,-6,-6,-6,-6,-6,-6,-6,-6,-6,
  274. -6,-6,-6,-6,-6,-6,-6,-6,-6,-6,-6,-6,-6,-6,-6,-6,-6,-6,-6,-6,-6,-6,-6,-6,-6,-6,
  275. -6,-6,-6,-6,-6,-6,-6,-6,-6,-6,-6,-6,-6,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,-6,-6,-6,
  276. -6,-6,-6,-6,-6,-6,-6,-6,-6,-6,-6,-6,0};  /* -6 = width=30 always */
  277. /* width = this*6 normal, this*4 if subscript/superscript. 0 = not printing */
  278. /* for LQ1050+: normal/condensed chars/line: 10cpi 136/233; 12cpi 164/272, 15cpi
  279.  204/NA, proportional 4890/9780 units as above (= 136/272 'n''s) */
  280. /*----- convert char # to screen col # */
  281. int line::to_tabexp(reg int c,int proportional/*=0*/) {
  282. reg int i,j,J,k; reg char*S=s;
  283. if(!proportional) {c<?=n; if(!B->tabtype) return c;
  284.     for(j=k=0;k<c;) if(S[k++]==9) j=totab(j); else j++; return j;}
  285. else {for(J=j=k=0;k<c;)
  286.     if((i=S[k++])==9) {i=j; j=totab(j); J+=(j-i)*chwidth[' '];}
  287.     else {j++; J+=abs(chwidth[i&127]);} return j;}}
  288. /*-----*/
  289. int line::to_scrcolno(int c) {return to_tabexp(c)-B->startc;}
  290. /*----- convert screen col # to char # */
  291. int line::from_tabexp(int p,int proportional/*=0*/) {
  292. reg int i,j,J,k; reg char *S=s;
  293. if(!proportional) {if(!B->tabtype) return p;
  294.     for(j=k=0;k<n?j<p:0;) if(S[k++]==9) j=totab(j); else j++; return k;}
  295. else {reg int P=p*6; for(J=j=k=0;k<n;) {if(J>=P) return k>0?k-1:0;
  296.     if((i=S[k++])==9) {i=j; j=totab(j);  J+=(j-i)*chwidth[' '];}
  297.     else {j++; J+=abs(chwidth[i&127]);}} return k;}}
  298. /*-----*/
  299. int line::from_scrcolno(int p) {return from_tabexp(p+B->startc);}
  300. /*----- correlate screen cursor with buffer cursor positions in line */
  301. void line::dotty() {
  302. if(B->dotcc<0) {B->dot.c<?=B->dot.r->n; B->dotcc=to_scrcolno(B->dot.c);}
  303. else B->dot.c=from_scrcolno(B->dotcc);}
  304. /*----- clear whole screen */
  305. void clearscreen(){reg short int i,c=sch(' ',White),n=gp_Cols*gp_Rows;
  306.     for(i=0;i<n;i++) screen[i]=c; for(i=0;i<gp_Rows;i++) Sl[i].ok=0;}
  307. /*----- clear this buffer's window only */
  308. void buffer::clearscreen(){reg int i; for(i=row1;i<=lastrow;i++) gp_clear(i);}
  309. /*----- buffer[int]: nth line in buffer */
  310. line* buffer::operator[](reg int n){reg int i; reg line *L;
  311. for(i=0,L=text.next;L;L=L->next,i++) if(i==n) return L; return 0;}
  312. /*----- go to this buffer */
  313. void buffer::go_to(){
  314. if(B==this) return; if(nrows) MOAN("this buffer already has a window");
  315. row1=B->row1; nrows=B->nrows; lastrow=B->lastrow; B->row1=B->nrows=B->lastrow=0;
  316. window[currentwindow]=B=this; B->redraw_info();}
  317. /*----- split window with this buffer, old window keeps N screen lines */
  318. void buffer::split_window_with(int N){reg int i,P=B->nrows-N;
  319. if(P<2 ?1: N<2) MOAN("window must have at least 2 lines");
  320. if(nrows) MOAN("this buffer already has a window");
  321. for(i=B->row1;i<=B->lastrow;i++) Sl[i].ok=0;
  322. B->nrows=N; nrows=P; lastrow=B->lastrow; B->lastrow-=P; row1=B->row1+N;
  323. for(i=nwindows++;i>currentwindow;i--) window[i]=window[i-1];
  324. for(i=row1;i<=lastrow;i++) Sl[i].buf=this; window[++currentwindow]=B=this;}
  325. /*----- read a Text from file */
  326. void Text::read(char*f){int F,b=0,i,j,k,m,n=1,z=0; line*K,*L=new line(),*N;K=L;
  327. if((F=open(f,0x8001))<0) {pr(disp,"new buffer '%s'",f); Display=disp; goto Z;}
  328. BLOCK: if((m=::read(F,CW,strsize))<0) {close(F); MOAN("file fault on reading");}
  329. if(!m) goto Y; z+=m; i=-1;
  330. LINE: i++; if(i) n++; for(k=i;i<m;i++) if(CW[i]==LF) break;
  331. if(!(n%50)) {pr(disp,"%5d lines = %6d bytes of %s read",n,b,f);
  332.     display(disp,B->lastrow,0,Blue+8);}
  333. N=new line(); *L-*N; j=i; *N=val(CW+k,j-k); L=N;
  334. N->la(!k); if(i<m) {b+=N->n+1; goto LINE;} goto BLOCK;
  335. Y: pr(disp,"file '%s' read, %1d lines, %1d bytes",f,n,z); Display=disp;
  336. Z: if(F>=0) close(F); beg=K; end=L; beg->la(1);
  337. for(L=beg;K=L->next;L=K) if(K->la()) {(*L)-(*this); K=L;} else K->la(1);
  338. for(L=beg;L->next;L=L->next)
  339.   if(L->n?L->s[L->n-1]==CR:0) {if(!(--L->n)) {delete L->s; L->s=0;}}
  340.   else L->no_cr(1);}
  341. /*----- read buffer from file */ /*** also moan if >1 buffers on this file ***/
  342. void buffer::read(){reg int i,j; line *L=&text; int n=0; Text T;
  343. if(this!=B) MOAN("BUG: tried to read non-current buffer");
  344. if(!name) MOAN("BUG: buffer has no file"); T.read(name);
  345. changed=0; text.next=T.beg; text.prev=T.end; dot=bof();}
  346. /*----- write buffer to file */
  347. void buffer::write(){register int i,j; int F; line *L; int n=0;
  348. if(name[0]=='(') MOAN("error in savebuffer: buffer has no file");
  349. if(attrib(name)==3*256) MOAN("bad filename, or can't find file's directory");
  350. char b[4096]; reg char *s,*t=b+4096,*u,*v,*w;
  351. pr(b,"shall I write to file '%s'?",name); if(!yesno(b)) goto AA;
  352. if((F=open(name,0x8302))<0) MOAN("can't open file");
  353. for(i=1,v=b,L=text.next;L;i++,L=L->next) {
  354.     if(L->n) for(w=(u=L->s)+L->n;u<w;) {
  355.     if(v>=b+4096-2) {if(::write(F,b,v-b)<0) goto FAIL; v=b;}
  356.     *v++=*u++;}
  357.     if(!(i%50)){pr(CW,"%5d lines written",i);::display(CW,B->lastrow,0,Blue+8);}
  358.     if(!L->next) break;
  359.     if(!L->no_cr()) *v++=CR; *v++=LF;}
  360. if(v>b) if(::write(F,b,v-b)<0) goto FAIL;
  361. ::close(F); changed=0; AA: Sl[lastrow].ok=0; return;
  362. FAIL: ::close(F); MOAN("file fault on writing");}
  363. /*----- empty this buffer */
  364. void buffer::deltext(){text.prev->del(); dustbin=text.next;
  365. text.next=text.prev=new line(); /* leave one empty line */}
  366. /*-----*/
  367. int buffer::exists(){buffer*BB;
  368. for(BB=bhead;BB;BB=BB->next) if(BB==this) return 1; return 0;}
  369. /*-----*/
  370. buffer::~buffer(){while(dot.next) rem1mark(); delete name; del_every(val(this));
  371. deltext(); if(bound.n) delete bound.s; buffer**I;
  372. for(I=&bhead;*I;I=&((*I)->next)) if(*I==this) {*I=(*I)->next; break;}}
  373. /*----- put buffer into buffer chain */
  374. buffer*buffer::linkin(){line *L=new line(); text.next=text.prev=L;
  375. next=bhead; bhead=this; dot=mark(text.next,0); start=dot; return this;}
  376. /*----- move dot one in each direction */
  377. void buffer::left(){reg line *L; if(dot.c>0) {--dot.c; return;}
  378.     if(B->twod) MOAN("hit start of line");
  379.     if(L=dot.r->prev) dot.c=(dot.r=L)->n; else MOAN("hit start of buffer");}
  380. /*-----*/
  381. void buffer::right(){reg line *L; if(dot.c<dot.r->n) {++dot.c; return;}
  382.     if(B->twod) {(*dot.r)+=' '; dot.c++; B->changed=1; return;}
  383.     if(L=dot.r->next) {dot.c=0; dot.r=L;} else MOAN("hit end of buffer");}
  384. /*-----*/
  385. void buffer::up   (){if(!(dot.Up   ())) MOAN("hit start of buffer");}
  386. /*-----*/
  387. void buffer::down (){if(!(dot.Down ())) MOAN("hit end of buffer")  ;}
  388. /*----- move mark one in each direction */
  389. int mark::Left() {reg line *L; if(c>0   ) {--c; return 1;}
  390.     if(B->twod) return 0;
  391.     if(L=r->prev) {c=(r=L)->n; return 1;} return 0;}
  392. /*-----*/
  393. int mark::Right(){reg line *L; if(c<r->n) {++c; return 1;}
  394.     if(B->twod) {(*r)+=' '; c++; B->changed=1; return 1;}
  395.     if(L=r->next) {c=0; r=L; return 1;} return 0;}
  396. /*-----*//*** beware of dot left off end of line after up and down */
  397. int mark::Up()   {reg line *L=r->prev; if(!L) return 0; r=L; return 1;}
  398. /*-----*/
  399. int mark::Down() {reg line *L=r->next; if(!L) return 0; r=L; return 1;}
  400. /*----- copy a Text and all of its text */
  401. Text Text::copy(){line *J,*L,*M,*N,*Z=end;
  402. jmp_buf *oldbad=bad,killos; bad=&killos; if(setjmp(killos)) {
  403.     clear(); bad=oldbad; MOAN("memory got full in copying kill");}
  404. for(L=0,M=beg;M;M=M->next) {
  405.     N=new line(M); if(L) *L-*N; else J=N; L=N; if(M==Z) break;}
  406. bad=oldbad; return Text(J,N);}
  407. /*----- empty a Text */
  408. void Text::clear(){if(beg) if(end) {end->del(); dustbin=beg;} beg=end=0;}
  409. /*----- mark1<mark2 : find what text order marks are in */
  410. int mark::operator<(mark&N){return r==N.r?c<N.c:r->lineno()<N.r->lineno();}
  411. /*----- print a Text to debug file */
  412. /* void Text::print(){line *L; int i;
  413. for(L=beg;L;L=L->next) {
  414.     for(i=0;i<L->n;i++) putc(L->s[i],debug); putc('¬',debug);}
  415. putc(LF,debug);} */
  416. /*----- tell display to print this text color f on color b */
  417. void region::color(int f,int b){B->col.Reg=*this; B->col.fg=f; B->col.bg=b;};
  418. /*----- if region is back to front, reverse its ends */
  419. void region::right_order(){
  420. if(!beg.r) MOAN("BUG: right_order() with zero left argument");
  421. if(!end.r) MOAN("BUG: right_order() with zero right argument");
  422. if(beg<end) return; mark W(beg); beg=end; end=W;};
  423. /*----- also counts (lines on screen) which are new since last time displayed */
  424. int region::ok_to_delete(){reg line*L; reg int n; char s[64];
  425. for(n=0,L=beg.r;L;L=L==end.r?0:L->next) if(L->sl==line_notshown) n++;
  426. if(n) {B->display();
  427.     for(n=0,L=beg.r;L;L=L==end.r?0:L->next) if(L->sl==line_notshown) n++;}
  428. if(!n) return 1; pr(s,"OK to delete or kill %1d lines which are off screen?",n);
  429. return yesno(s);}
  430. /*----- delete text between the marks */
  431. mark region::Delete(int query/*=1*/){reg line *I,*J,*K,*L; reg mark *Z;
  432. if(beg==end) return beg;
  433. if(query) if(!ok_to_delete()) MOAN("user abort"); B->changed=1;
  434. if((I=beg.r)==end.r) {reg int x=end.c-beg.c;
  435.     forallmarks(Z) if(Z->r==I) if(Z->c>beg.c) Z->c=beg.c>?(Z->c-x);
  436.     beg<<=x; *I-=x; return mark(I,beg.c);}
  437. *I=beg.c; J=I->next; K=end.r; L=end/1; *I-*L; --*I;
  438. for(K->next=0;J;J=J->next) {forallmarks(Z) if(Z->r==J) {Z->r=beg.r;Z->c=beg.c;}}
  439. if(dustbin) *K-*dustbin; else K->next=0; dustbin=J; return mark(I,beg.c);}
  440. /*----- kill text between the marks */
  441. mark region::kill(int query/*=1*/){reg line *I,*J,*K,*L; mark *Z;
  442. if(query) if(!ok_to_delete()) MOAN("user abort");
  443. static mark prevkill(0,0); B->changed=1; int C=beg.c;
  444. int z=prevobtype!=ob_kill ? 0 : prevkill==beg ? 1 : prevkill==end ? -1 : 0;
  445. if(!z) {nkill++; nkill&=15; killring[nkill].clear();} Text&W=killring[nkill];
  446. if(beg.r==end.r) {int x; I=beg.r; J=K=new line(I->s+C,x=end.c-C);
  447.     forallmarks(Z) if(Z->r==I) if(Z->c>C) Z->c=C>?(Z->c-x);
  448.     beg<<=x; *I-=x; beg.r->la(1);}
  449. else {I=beg.r; J=beg/0; K=end.r; L=end/1; *I-*L; --*I;}
  450. J->prev=K->next=0; /* seal ends of cut-out bit */
  451. for(L=J;L;L=L->next) forallmarks(Z) if(Z->r==L) *Z=mark(I,C);
  452. if(!z ?1: !W.beg) {W.beg=J; W.end=K;}
  453. else if(z<0) {*K-*W.beg; W.beg=J; *K-W;}
  454. else /* if(z>0) */ {*W.end-*J; W.end=K; J=J->prev; *J-W;}
  455. prevkill=mark(I,C); return prevkill;}
  456. /*----- copy text between marks to Text */
  457. void region::copykill(){line *J,*K; char c=B->changed;
  458. nkill++; nkill&=15; Text&W=killring[nkill]; W.clear(); if(beg==end) return;
  459. if(beg.r==end.r) {K=new line(beg.r->s+beg.c,end.c-beg.c); W=Text(K,K);}
  460. else {J=beg/0; K=end.r; end/1; W=Text(J,K).copy(); -*J; --*K; B->changed=c;}}
  461. /*----- copy region to mark, with fudge to save & restore an affected kill */
  462. region region::copyto(mark&m){if(beg==end) return m-m;
  463. if(m.isin(*this)) MOAN("can't copy text to within itself");
  464. Text *K=&killring[nkill],T=*K; K->beg=K->end=0; nkill--; copykill();
  465. region r=m.yank(killring[nkill],0); killring[nkill]=T; return r;}
  466. /*----- move region to mark */ /* push & pop m & end to be corrected for edit */
  467. region region::moveto(mark&m){if(beg==end) return m-m; B->changed=1;
  468. mark M; if(m==end?1:m==beg) return *this;
  469. if(m.isin(*this))MOAN("can't move text to within itself"); m.push(); end.push();
  470. line *A1=beg.r,*A2=beg/0; end.pop(); line *B1=end.r,*B2=end/1;
  471. *A1-*B2; --*A1; M.pop(); return M.yank(Text(A2,B1),0);}
  472. /*----- insert (copy of) Text at mark */
  473. region mark::yank(const Text&k,int copy/*=1*/){
  474. line *L=r,*M; Text K; mark Snip=*this,Z; K=k;
  475. if(!K.beg) return Snip-Snip; if(copy) K=K.copy(); M=Snip/1; B->changed=1;
  476. *L-*K.beg; *K.end-*M; mark(M,0).push(); --*L; -*M; Z.pop(); return Snip-Z;}
  477. /*-----*/
  478. int mark::isin(reg region &RR){reg line*L,*A=RR.beg.r,*R=r,*Z=RR.end.r;
  479. if(R==A) if(c<RR.beg.c) return 0; if(R==Z) return c<RR.end.c; if(R==A) return 1;
  480. if(A==Z) return 0; for(L=A->next;L!=Z;L=L->next) if(L==R) return 1; return 0;}
  481. /*-----*/
  482. byte to__upper[256],to__lower[256]; char chtype[256];
  483. region Found;
  484. /*-----*/
  485. /* void pq(char*s,int n){int i;
  486. putc('"',debug); for(i=0;i<n;i++) putc(s[i],debug); putc('"',debug);} */
  487. /*-----*/
  488. /* Micro-Emacs has : [ = char class start, - = range in [], ] = end of char
  489. class, ^ = negate char class, * = "closure, does not extend past newline", & =
  490. use matched string in replacement */
  491. /* try * = any chars: limit how many?: magic number = count & count limits? */
  492. /*-----*//* returns region -> string found */
  493. int heremagic(mark A,val T){if(!T.magic(0)) if(T.s[0]!=*A) return 0;
  494. int i,ce,nm=0,b=0,d,sc; mark M(A),N; char sk,C=*M;
  495. reg char c,*t=T.s,*te=T.s+(T.n&0x00ffffff); byte D; line*L; Found=A-=mark(0,0);
  496. if(!M.r) return 0; mark B[17]; B[0]=A;
  497. if(T.magic(0)) if(*t=='/') if(!M.c) t++; /* leading magic '/', at bol already */
  498. CH: if(Breakin()) MOAN("user abort"); if(t>=te) goto OK;
  499. if(!T.magic(t-T.s)) if(C!=*t++) goto NO; else goto NEXT;
  500. switch(D=*t++) {
  501. default: if(isalpha(D))if(D==to_lower(D)){if(D==to_lower(C))goto NEXT; goto NO;}
  502.     Moan="illegal magic character in search: ' '"; Moan[36]=t[-1]; MOAN(Moan);
  503. case '[': if(b>=16) MOAN("magic [] > 16 deep"); B[++b]=M; goto CH;
  504. case '|': if(b<1) MOAN("magic | outside [ ]");
  505.     for(d=b;t<te;t++) if(T.magic(t-T.s)) if((c=*t)=='[') b++;
  506.   else if(c==']') if(--b<d) {t++; goto CH;} goto OK; /* OK to |, skip to ] */
  507. case ']': if(--b<0) MOAN("unpaired magic ]"); goto CH;
  508. case '{': Found.beg=M; goto CH;
  509. case '}': Found.end=M; goto CH;
  510. case '=': M.c=M.r->n; C=LF; goto CH;
  511. case '#': if(isalnum(C)) goto NO; goto NEXT;
  512. case ',': if(!isalnum(C)) goto NO; goto NEXT;
  513. case '/': if(M.c==M.r->n) goto NEXT; /* falls thru */
  514. case '\\': if(!M.r->prev) if(!M.c) goto CH;
  515.        if(!M.r->next) if(M.c==M.r->n) goto CH; goto NO;
  516. case 9:   sk=9; goto SKIP;
  517. case ' ': sk=' '; goto SKIP;
  518. SKIP: sc=0; SK: if(C==' '?:C==9) {++M; C=*M; sc++; goto SK;}
  519.     if(C==LF) if(sk==9) if(N=M,++M,M.r) {sc++; C=*M; goto SK;} else M=N;
  520.     if(!sc) goto NO; goto CH;
  521. case '.': if(C==LF) goto NO; goto NEXT;}  goto CH;
  522. NEXT: N=M; ++M; if(!M.r) {M=N; goto NO;} C=*M; goto CH;
  523. NO: if(b>0) for(d=b;t<te;t++) if(T.magic(t-T.s)) /* skip to | for alt to try */
  524.        if(*t=='[') b++; /* allow omitted initial [ and final ] */
  525.   else if(*t=='|') {if(b<=d) {M=B[b]; C=*M; t++; goto CH;}}
  526.   else if(*t==']') {if(b) d<?=--b;} return 0;
  527. OK: if(b>0) MOAN("unpaired magic ["); if(!Found.end.r) Found.end=M;
  528. if(Found.end<Found.beg) MOAN("magic {range} back-to-front"); return 1;}
  529. /*-----*//* returns mark -> end of string found */
  530. int here(mark A,val T,int word/*=0*/){
  531. reg char*s=A.r->s+A.c,*se,*t=T.s,*te=t+T.n;
  532. if(word) if(A.c>=word) if(isalnum(s[-word])) return 0; Found.beg=A;
  533. if(Breakin()) MOAN("user abort"); L: se=(A.r->s+A.r->n)<?(s+(te-t));
  534. if(B->Case) while(s<se) {if(*s++!=*t++) return 0;}
  535. else while(s<se) if(to_upper(*s++)!=to_upper(*t++)) return 0;
  536. if(t>=te) {Found.end=mark(A.r,s-A.r->s); return word?!isalnum(*Found.end):1;}
  537. if(!(A.r=A.r->next)) return -1; if(*t++!=LF) return 0; s=A.r->s; goto L;}
  538. /*----- search forwards for text in region */
  539. int region::huntf(val T,int word/*=0*/) {if(!T.n) return 0; mark M; region R;
  540. reg char *s,*se,C; line*A=beg.r,*D; if(!A) return 0; int a=beg.c,v=0,i,n; val U;
  541. line*e=end.r; line*E=e->next; e->next=0; int en=e->n; e->n=end.c; /*pseudo eof*/
  542. jmp_buf*oldbad=bad,failed; bad=&failed; if(setjmp(*bad)) goto F;
  543. if(T.magic()) if(T.magic(0)?0:T.s[0]==LF) {for(;A;A=A->next)
  544.       if(heremagic(mark(A,A->n),T)) {v=1; goto F;}  goto F;}
  545. else {for(;A;A=A->next,a=0) for(n=A->n;a<=n;a++)
  546.       if(heremagic(mark(A,a   ),T)) {v=1; goto F;}  goto F;}
  547. C=B->Case?T.s[0]:to_upper(T.s[0]); U=val(T.s+1,T.n-1); if(word) word=2;
  548. if(C==LF) {for(;D=A->next;A=D) if(i=here(mark(D,0),U),i>0) {
  549.     Found.beg=mark(A,A->n); v=1; goto F;} else if(i<0) goto F;}
  550. else if(B->Case) {for(;A;A=A->next,a=0) for(se=A->s+A->n,s=A->s+a;s<se;s++)
  551.       if(         *s ==      C) if(i=here(mark(A,s-A->s+1),U,word),i>0) {
  552.     Found.beg.c--; v=1; goto F;} else if(i<0) goto F;}
  553. else             {for(;A;A=A->next,a=0) for(se=A->s+A->n,s=A->s+a;s<se;s++)
  554.       if(to_upper(*s)==(byte)C) if(i=here(mark(A,s-A->s+1),U,word),i>0) {
  555.     Found.beg.c--; v=1; goto F;} else if(i<0) goto F;}
  556. F: bad=oldbad; e->next=E; e->n=en; /* remove pseudo eof */ if(Moan) MOAN(Moan);
  557. return v;}
  558. /*----- search backwards for text in region */
  559. int region::huntb(val T,int word/*=0*/) {if(!T.n) return 0; mark M; region R;
  560. reg char *s,*sb; line*e=end.r,*D,*A,*b=beg.r?:B->text.next; if(!e) return 0;
  561. int a=end.c,v=0,n,i; val U; reg char C; static char X;
  562. line*E=e->next; e->next=0; int en=e->n; e->n=end.c; /* pseudo eof */
  563. line*BB=b->prev; b->prev=0; b->s+=beg.c; b->n-=beg.c; /* make a pseudo bof */
  564. jmp_buf*oldbad=bad,failed; bad=&failed; if(setjmp(*bad)) goto F;
  565. if(T.magic()) if(T.magic(0)?0:T.s[0]==LF) {for(A=e;A;A=A->prev)
  566.       if(heremagic(mark(A,A->n),T)) {v=1; goto F;}  goto F;}
  567. else {for(A=e;A;A=A->prev) for(a=A->n;a>=0;a--)
  568.       if(heremagic(mark(A,a   ),T)) {v=1; goto F;}  goto F;}
  569. C=B->Case?T.s[0]:to_upper(T.s[0]); U=val(T.s+1,T.n-1); if(word) word=2;
  570. if(C==LF) {for(A=e;D=A,A=A->prev;) if(here(mark(D,0),U,word)>0) {
  571.     Found.beg=mark(A,A->n); v=1; goto F;}}
  572. else if(B->Case) {for(A=e;A;A=A->prev) for(s=(sb=A->s?:&X)+A->n-1;s>=sb;s--)
  573.       if(         *s ==      C) if(here(mark(A,s-A->s+1),U,word)>0) {
  574.       Found.beg.c--; v=1; goto F;}}
  575. else             {for(A=e;A;A=A->prev) for(s=(sb=A->s?:&X)+A->n-1;s>=sb;s--)
  576.       if(to_upper(*s)==(byte)C) if(here(mark(A,s-A->s+1),U,word)>0) {
  577.       Found.beg.c--; v=1; goto F;}}
  578. F: bad=oldbad; e->next=E; e->n=en; b->prev=BB; b->s-=beg.c; b->n+=beg.c;
  579. /* remove pseudo bof & eof */ if(Moan) MOAN(Moan); return v;}
  580. /*** treats e.g. 'fi' as word 'i' if mark beg is at the i. Similarly huntf. */
  581. /*-----*/
  582. void line::de_trailing_space(){mark M(this,n),N(this,n);
  583. N.skip_white(1,1); (N-M).Delete(0);}
  584. /*-----*/
  585. void mark::skip_white(int back/*=0*/,int Eol/*=0*/){
  586. if(back) while(!c   ?(Eol?r->prev!=0:0):mark(r,c-1).is_white()) --*this;
  587. else     while(eol()?(Eol?r->next!=0:0):            is_white()) ++*this;}
  588. /*-----*/
  589. void mark::find_white(int back/*=0*/){
  590. if(back) while(!c   ?0:!mark(r,c-1).is_white()) --*this;
  591. else     while(eol()?0:!            is_white()) ++*this;}
  592. /*-----*/
  593. void mark::skip_alnum(int back/*=0*/){
  594. if(back) while(!c   ?0:mark(r,c-1).is_alnum()) --*this;
  595. else     while(eol()?0:            is_alnum()) ++*this;}
  596. /*-----*/
  597. void mark::find_alnum(int back/*=0*/,int Eol/*=0*/){
  598. if(back) while(!c   ?(Eol?r->prev!=0:0):!mark(r,c-1).is_alnum()) --*this;
  599. else     while(eol()?(Eol?r->next!=0:0):!            is_alnum()) ++*this;}
  600. /*-----*/
  601. void line::split_if_long(){int nn; char *S; line *L=this,*N; mark M,P;
  602. B->dotcc=-1;
  603. AGAIN: nn=L->from_tabexp(abs(B->rmargin)-1,B->rmargin<0); if(L->n<=nn) return;
  604. M=mark(L,nn); if(M.is_white()) M.skip_white(0); else M.find_white(1);
  605. if(!M.c) {M.find_white(0); M.skip_white();}
  606. if(M.c<L->n) {N=M/2; L->de_trailing_space(); L=N; goto AGAIN;}}
  607. /*----- turn val into Text */
  608. Text::Text(val&T){line *J,*K,*L; int i=0,j,n=T.n; char *t=T.s;
  609. K=new line(); J=K; AGAIN: for(j=i;i<n;i++) if(t[i]==LF) break;
  610. *K=val(t+j,i-j); if(i>=n) goto OUT; L=new line(); *K-*L; K=L; i++; goto AGAIN;
  611. OUT: beg=J; end=K;}
  612. /*----- replace text between the marks */
  613. region region::repl(Text&k,int copy/*=1*/){B->changed=1;
  614. line *I,*J,*K,*L; mark M,*Z; int x; Text T(k);
  615. if((I=beg.r)==end.r) {L=beg/0; x=end.c-beg.c;
  616.     forallmarks(Z) if(Z->r==L) Z->c=(Z->c-x)>?0; mark(L,0)<<=x; *L-=x;}
  617. else {*I=beg.c; J=I->next; K=end.r; L=end/1; K->next=0;
  618.     for(;J;J=J->next) {forallmarks(Z) if(Z->r==J) *Z=beg;}
  619.     K->del(); dustbin=J;}
  620. if(!T.beg) {*I-*L; --*I; return beg-beg;} if(copy) T=T.copy();
  621. *I-*T.beg; *T.end-*L; mark(L,0).push(); --*I; -*L; M.pop(); return beg-M;}
  622. /*----- replace all occurences of Old by New in region */
  623. void region::replace(val Old,val New,int ask/*=0*/,int word/*=0*/){
  624. int i,j,k,w=word>?0; mark Q;
  625. if(!Old.n) MOAN("tried to search-&-replace nil");
  626. static char*notword="object searched for is not a word";
  627. static char*Z="\
  628. space = yes, N = no, . = yes & exit, alt-end = no & exit, end = no & stay here";
  629. if(word>0) {if(Old.magic()) MOAN(notword);
  630.     for(i=0;i<Old.n;i++) if(!isalnum(Old.s[i])) MOAN(notword);}
  631. line *L; int c=' '; B->dot.push(); j=k=0; Q=beg;
  632. NO: if(!(Q-=end).huntf(Old,word)) goto OUT; Q=Found.end; j++;
  633. if(ask) {B->dot=Found.beg; B->dotcc=-1; Found.color(White,Blue);
  634.     B->display(); showmoan();
  635. X:  display(Z,B->lastrow,0,Cyan); Sl[B->lastrow].ok=1;
  636.     switch(c=getkey()) {
  637.     case -::end: Q.pop(); goto EXIT;
  638.     case -alt_end: goto OUT;
  639.     case 'N': case 'n': goto NO;
  640.     case ' ': case '.': goto F;
  641.     default: if(!Breakin()) goto X;}}
  642. F: end.push(); Q=Found.repl(Text(New)).end; k++; end.pop(); if(c==' ') goto NO;
  643. OUT: B->dot.pop(); EXIT: B->dotcc=-1; Sl[B->lastrow].ok=0;
  644. pr(disp,"%1d found, %1d replaced",j,k); Display=disp;}
  645. /*-----*/
  646. val&getkeyseq(val P){int z=1,j,c=1; keyarray *keytable=&keys;
  647. val *Key; display(P,B->lastrow,0,Blue+8); c=getkey();
  648. ARRAY: if(c<0) {keyseqc[z++]=0; keyseqc[z++]=-c; Key=&(*keytable)[0][-c];}
  649. else {keyseqc[z++]=c; Key=&(*keytable)[c];} keyseqc[0]=z;
  650. if(Key->n!=_keyarray) return *Key;
  651. if(c){pr(CW,"%v %k   (more reqd)",&P,&keyseq); display(CW,B->lastrow,0,Blue+8);}
  652. c=getkey(); if(c>0) c=to_upper(c); keytable=Key->k; goto ARRAY;}
  653. /*----- look up val in key table, if bound to a keysequence */
  654. val&val::keyseq(){int j=1,nn; val*Key; keyarray *kt=&keys; static val X(0,_bad);
  655. if(n==_char) {if(i&~255) Key=&keys[0][i&255]; else Key=&keys[i&255];
  656.     if(Key->n==_keyarray) {X.s="end of keysequence missing"; return X;}
  657.     return *Key;}
  658. if(n!=_keyseq?:(nn=s[0])<2) {X.s="subr arg is not a keysequence"; return X;}
  659. byte C=s[1],D; ARRAY: keyseqc[j++]=C; Key=&(*kt)[C];
  660. if(Key->n!=_keyarray) {if(j!=nn) {X.s="extra keys after keysequence"; return X;}
  661.     keyseqc[0]=j; return *Key;}
  662. if(j>=nn) {X.s="end of keysequence missing"; return X;}
  663. D=C; C=s[j]; if(D) C=to_upper(C); kt=Key->k; goto ARRAY;}
  664. /*----- ask user for string arg if necessary */
  665. val val::getifn(val&W,char*prompt,int type/*=0*/,char*start/*=0*/){
  666. char*w,*zap="magic string not allowed here"; int i,max; buffer*C; val Z;
  667. if(n?0:!s) {max=strsize;
  668.     if(start) {max=-max; strcpy(W.s,start); W.n=strlen(W.s);}
  669.     getstring(prompt,B->lastrow,W,max,type); n=W.n; s=W.s;}
  670. else if(n<0) if(!magic()) MOAN("subroutine arg is not a string");
  671. if(type!=_magic) if(magic()) MOAN(zap);
  672. switch(type) {default: return val();
  673. case _subr:
  674.     if(!n) {strcpy(s=W.s,(Z=subrmenu(prompt),Z.S->name)); n=W.n=strlen(s);}
  675.     else Z=named(*this);
  676.     if(Z.n!=_Subr) if(Z.n!=_macro) MOAN("not subroutine and not macro");
  677.     return Z;
  678. case _buffer: C=0;
  679.     if(W.n==1) if(W.s[0]=='=') {strcpy(W.s,B->name); W.n=strlen(W.s);}
  680.     w=s; s=W.s;
  681.     if(!n) strcpy(s,(C=buffer_menu(prompt))->name); else fullfilename(s,w);
  682.     if(attrib(s)==3*256) MOAN("can't find this file's directory");
  683.     W.n=strlen(s); if(attrib(s)&16) filefromdirmenu(W,prompt,B->name); n=W.n;
  684.     return C?val(C):val();}}
  685. /*-----*/
  686. void getstring(char*prompt,int sl,val &T,int max,int type/*=0*/){
  687. #define MoaN(s) ({Moan=s; goto BAD;})
  688. int c=0,i,j,k=-1,l,M,N=T.n&0x00ffffff,n=0,prmag=0,ptl=strlen(prompt); short*sd;
  689. int oldN=N,pp,C; line *L; val*f; char *s,*SC; mousestate ms; ms=Jerry;
  690. extern char*getstringhelp[],*magichelp[]; if(max<0) {max=-max; n=N; k++;}
  691. if(!T.s) T.s=new char[max]; uns short*Sc=Sl[sl].sa,S[strsize+1];
  692. for(i=0;i<N;i++) S[i]=T.s[i]&255; pp=(gp_Cols-ptl)/2; Jerry.mc=1;
  693. if(T.magic()) {s=T.s+N+1; for(i=0;i<N;i++) if(bit(s,i)) S[i]|=256;}
  694. GETCHAR: for(i=0;i<ptl;i++) Sc[i]=sch(prompt[i],Magenta);
  695. Jerry.range(4,(N+1)<?2048);
  696. C=n<=2*pp?0:((n-pp/2)/pp)*pp; M=N; X: j=((N-C)<?(gp_Cols-ptl));
  697. for(i=0;i<j;i++) Sc[i+ptl]=sch(S[C+i]&255,((S[C+i]&256)?Orange+8:Cyan));
  698. for(i=0;i<j;i++) if(l=S[C+i]-256,l==' '?:l==255?:!l) Sc[i+ptl]=254|(Orange<<12);
  699. if(C) Sc[ptl]=sch(17,Red); if(N-C>gp_Cols-ptl) Sc[gp_Cols-1]=sch(16,Red);
  700. gp_clear(sl,ptl+N-C); Y: cursor.r=sl; cursor.c=((n-C+ptl)>?0)<?(gp_Cols-1);
  701. if(k<0) display("(alt-minus for help)",sl,(ScreenCols()-21)>?cursor.c,Green+8);
  702. if(c!=-mousemove) Jerry.move(0,n); gp_cursor(cursor); c=getkey(); if(!++k) N=0;
  703. switch(c){ /* special char */
  704. case -alt_R: if(T.s!=T2w ?: T1t.n<=0 ?: T1t.s!=T1w) break;
  705.     if(max-N<T1t.n) MoaN("text is too long to fit in reply");
  706.     for(i=N-1;i>=n;i--) S[i+T1t.n]=S[i]; for(i=0;i<T1t.n;i++) S[n+i]=T1w[i]&255;
  707.     n+=T1t.n; N+=T1t.n; break;
  708. case -alt_K: f=&getkeyseq("type key sequence whose name you want to insert: ");
  709.     pr(CW,"%k",&keyseq);
  710.     if(max-N<(c=strlen(CW))) MoaN("buffer name is too long to fit in reply");
  711.     for(i=N-1;i>=n;i--) S[i+c]=S[i]; for(i=0;i<c;i++) S[n+i]=CW[i]&255;
  712.     n+=c; N+=c; break;
  713. case -alt_C: s=B->name; goto Z;
  714. case -alt_B: f=&getkeyseq("key sequence bound to a buffer:");
  715.     if(f->n!=_buffer) {pr(CW,"%k is not bound to a buffer",&keyseq); MoaN(CW);}
  716.     s=f->b->name;
  717. Z:  if(max-N<(c=strlen(s))) MoaN("buffer name is too long to fit in reply");
  718.     for(i=N-1;i>=n;i--) S[i+c]=S[i]; for(i=0;i<c;i++) S[n+i]=s[i]&255;
  719.     n+=c; N+=c; break;
  720. case -alt_Y: /* insert last kill */
  721.     for(c=-1,L=killring[nkill].beg;L;L=L->next) c+=L->n+1;
  722.     if(max-N<c) MoaN("kill is too long to fit in reply");
  723.     for(i=N-1;i>=n;i--) S[i+c]=S[i]; N+=c;
  724.     for(L=killring[nkill].beg;L;L=L->next) {s=L->s;
  725.     for(i=0;i<L->n;i++) S[n++]=s[i]&255; if(L->next) S[n++]=LF;} break;
  726. case -del:     if(N) {for(i=n+1;i<N;i++) S[i-1]=S[i]; --N; n<?=N;} break;
  727. case -alt_del: if(n) {for(i=n--;i<N;i++) S[i-1]=S[i]; --N; n<?=N;} break;
  728. case -leftarrow: if(n>0) n--; break;
  729. case -rightarrow: n++; n<?=N; break;
  730. case -home: n=0; break;
  731. case -end: if(!k) N=M; n=N; break; /* if end first, keep the old reply */
  732. case -f1: if(n<N) S[n]^=256; break;
  733. case -f2: if(n>0) S[n-1]^=256; break;
  734. case -f3: prmag=256-prmag; break;
  735. case -alt_ret: c=CR; goto C;
  736. case -alt_end: case 0: T.n=0; MoaN("user abort");
  737. case -alt_minus: sd=screendump();
  738.     for(i=0;SC=getstringhelp[i];i++) display(SC,i,0,Cyan);
  739.     display("(type any key to continue)",0,52,Cyan); getkey();
  740.     screenrestore(sd); sd=screendump();
  741.     for(i=0;SC=magichelp[i];i++) display(SC,i,0,Cyan);
  742.     display("(type any key to continue)",0,52,Cyan); getkey();
  743.     screenrestore(sd); break;
  744. case -ctrl_tab: i=B->dotcc; j=B->dot.c; c=charfrommenu(); refreshscreen();
  745.     B->dotcc=i; B->dot.c=j; /* cancel effect of dotty() */ if(c<0) break;
  746. case -mousemove: k--; n=Jerry.x; j=C; C=n<=2*pp?0:((n-pp/2)/pp)*pp;
  747.     if(j==C) goto Y; else goto X;
  748. case -lbuttond: case -mbuttond: case -rbuttond: k--; goto X;
  749. case -lbutton: if(!k) if(type==_buffer) {S[0]='.'; S[1]=0; N=1; k=0;} /*run on*/
  750.     else break; /* get a dir menu */
  751. case -pagedown: Sl[sl].ok=0; T.n=N=k?N:M; s=T.s+N+1;
  752.     for(i=0;i<N;i++) T.s[i]=S[i]; for(i=lmagic(N)-1;i>=N;i--) T.s[i]=0; k=0;
  753.     for(i=0;i<N;i++) if(S[i]&256) {s[i>>3]|=(128>>(i&7)); k=1;}
  754.     if(k) T.n=N|0x80000000; Jerry=ms; return; /* exit */
  755. default: if(c&~255) goto GETCHAR; if(c==CR) c=LF; /* so RET makes end-of-line */
  756. C:  if(N<max) {for(i=N++;i>n;i--) S[i]=S[i-1]; S[n++]=(c&255)+prmag;}}
  757. N>?=n; N<?=max; goto GETCHAR;  BAD: Jerry=ms; MOAN(Moan);}
  758. /*-----*/
  759. char*getstringhelp[]={
  760. "[SPECIAL KEYS USED WHEN INPUTTING STRING ARGUMENTS]",
  761. "printing & control chars & space & newline go into the string argument",
  762. "alt-B          then keysequence bound to buffer, inserts that buffer's name",
  763. "alt-C          inserts the current buffer's name",
  764. "alt-K          then keysequence: inserts that keysequence's name",
  765. "alt-R          in 2nd arg of replace commands, insert 1st arg just entered",
  766. "alt-Y          insert last kill",
  767. "delete         delete this character (at end of string, previous character)",
  768. "alt_delete     delete previous character",
  769. "\033              go left one character",
  770. "\032              go right one character",
  771. "home           go to start of string",
  772. "end            go to end of string. If first keypunch, do not lose old string",
  773. "alt_end        abort the command that asked for this string argument",
  774. "ctrl_tab       menu to input a character",
  775. "F1             change char from normal (cyan) to magic (yellow) or vice-versa",
  776. "F2             ditto but previous char (usually the character just typed)",
  777. "F3             start or stop typing chars as magic",
  778. "pagedown       accept string as it is",
  779. "alt_ret        put a ctrl-M (CR) (carriage-return) into the string as a byte",
  780. "ret or ctrlJ or ctrlM    put a ctrl-J (LF) (end-of-line) into the string",
  781. 0};
  782. /*-----*/
  783. byte esc_alt[26]={alt_A,alt_B,alt_C,alt_D,alt_E,alt_F,alt_G,alt_H,alt_I,
  784.     alt_J,alt_K,alt_L,alt_M,alt_N,alt_O,alt_P,alt_Q,alt_R,alt_S,alt_T,alt_U,
  785.     alt_V,alt_W,alt_X,alt_Y,alt_Z};
  786. /*-----*/
  787. void out_of_store(){Moan="out of store"; longjmp(*bad,1);}
  788. /*----- replace all tabs by spaces in region */
  789. void region::expandtabs(){B->changed=1;
  790. reg int i,j,k,m; int b,e,D,E,n,N; mark *M; line *L; reg char*s,*t,c; short *mc;
  791. for(L=beg.r,b=beg.c;L;L=L->next,b=0) {
  792.     L->la(1); s=L->s; n=L->n; e=L==end.r?end.c:n; m=0;
  793.     for(D=i=0  ;i<b;i++) if(s[i]==9)  D=totab(D);       else D++;
  794.     for(E=D,i=b;i<e;i++) if(s[i]==9) {E=totab(E); m=1;} else E++;
  795.     if(!m) goto NEXT;
  796.     if(!(j=E-D-e+b)) {for(i=b;i<e;i++) if(s[i]==9) s[i]=' '; goto NEXT;}
  797.     t=new char[roundup(N=n+j)]; mc=new short[n+1];
  798.     for(i=0;i<b;i++) t[mc[i]=i]=s[i];
  799.     for(j=D,m=i=b;i<e;i++) {mc[i]=m; if((c=s[i])==9)
  800.     for(k=j,j=totab(j);k<j;k++) t[m++]=' '; else {j++; t[m++]=c;}}
  801.     for(i=e;i<n;i++) t[mc[i]=m++]=s[i]; mc[n]=m;
  802.     forallmarks(M) if(M->r==L) M->c=mc[M->c];
  803.     delete mc; L->s=t; delete s; L->n=N;
  804.     NEXT: if(L==end.r) return;}}
  805. /*----- replace spaces by tabs in region */
  806. void region::maketabs(){B->changed=1;
  807. reg int i,j,k,m; int b,e,D,E,n,N; mark *M; line *L; reg char*s,*t,c; short *mc;
  808. for(L=beg.r,b=beg.c;L;L=L->next,b=0) {
  809.     L->la(1); s=L->s; n=L->n; e=L==end.r?end.c:n; m=0;
  810.     for(D=i=0  ;i<b;i++) if(s[i]==9) D=totab(D); else D++;
  811.     for(E=D,i=b;i<e;i++) if(s[i]==9) E=totab(E); else E++;
  812.     j=E-D-e+b; t=new char[roundup(N=n+j)]; mc=new short[n+1];
  813.     for(j=D,i=b;i<e;i++) {mc[i]=j; if(s[i]==9) j=totab(j); else j++;}
  814.     for(i=b;i<e;i++) if(s[i]==' ') if(!sptotab(mc[i])) s[i]=9;
  815.     for(i=e-1;i>b;i--) if(s[i]==9 ?1: mc[i]==-2) if(s[i-1]==' ') mc[i-1]=-2;
  816.     for(i=b>?1;i<e;i++) if(s[i]==9) if(!sptotab(mc[i])) if(mc[i-1]!=-2)s[i]=' ';
  817.     for(i=0;i<b;i++) t[mc[i]=i]=s[i]; m=b;
  818.     for(i=b;i<e;i++) {k=mc[i]; mc[i]=m; if(k>=0) t[m++]=s[i];}
  819.     for(i=e;i<n;i++) t[mc[i]=m++]=s[i]; mc[n]=m;
  820.     forallmarks(M) if(M->r==L) M->c=mc[M->c]; delete mc;
  821.     L->s=t; delete s; L->n=m; if(L==end.r) return;}}
  822. /*-----*/
  823. short*screendump(){int i=ScreenRows()*ScreenCols(); short*s=new short[i];
  824.     while(--i>=0) s[i]=screen[i]; return s;}
  825. void screenrestore(short*s){int i=ScreenRows()*ScreenCols();
  826.     while(--i>=0) screen[i]=s[i]; delete s;}
  827. /*-----*/
  828.